package com.example.rbq.client

import android.content.Context
import android.net.wifi.WifiManager
import android.util.Log
import com.example.lib_core.util.HexString
import com.example.lib_core.util.KitUtil
import java.io.IOException
import java.lang.reflect.InvocationTargetException
import java.lang.reflect.Method
import java.net.DatagramPacket
import java.net.DatagramSocket
import java.net.InetAddress


class Emulator(val option: Option){

    var udpSocket: DatagramSocket? = null

    var isOpened = false
        private set

    @Throws(LinkException::class)
    fun open() {
        val option = option
        debug("open()")
        try {
            if (isOpened) throw LinkException("already opened")
            udpSocket = DatagramSocket(option.clientPort)
            val address = udpSocket?.inetAddress
            Log.e("xiao","client socket address:$address")
            isOpened = true
        } catch (e: LinkException) {
            throw e
        } catch (e: Exception) {
            throw LinkException(e)
        }
    }

    fun close() {
        debug("close()")
        isOpened = false
        if (udpSocket != null) {
            udpSocket!!.close()
            udpSocket = null
        }
    }

    @Throws(LinkException::class)
    fun sendTo(buffer: ByteArray) {
        if (!isOpened) {
            debug("link is closed")
            return
        }

        try {
            write(buffer, 0, buffer.size)
        } catch (e: Exception) {
            throw LinkException(e)
        }
    }

    private fun write(buffer: ByteArray, start: Int = 0, length: Int = buffer.size) {
        val socket = udpSocket
        if (socket == null) {
            debug("socket is null")
            return
        }

        val sendBuffer = ByteArray(length)
        System.arraycopy(buffer, start, sendBuffer, 0, length)

        val packet = DatagramPacket(sendBuffer, sendBuffer.size)
        packet.setData(sendBuffer, 0, sendBuffer.size)
        packet.port = option.servicePort
        packet.address = InetAddress.getLocalHost()
        try {
            socket.send(packet)
            debug("client send $length bytes to ${packet.address}: ${HexString.valueOf(buffer)}")
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }

    protected fun debug(message: String) {
        if (DEBUG) println("Emulator: $message")
    }

    fun sendBroadcast(buffer: ByteArray){
        val socket = udpSocket
        if (socket == null) {
            debug("socket is null")
            return
        }
        socket.broadcast = true
        val length = buffer.size
        val sendBuffer = ByteArray(length)
        System.arraycopy(buffer, 0, sendBuffer, 0, length)

        val packet = DatagramPacket(sendBuffer, sendBuffer.size)
        packet.setData(sendBuffer, 0, sendBuffer.size)
        packet.port = option.servicePort
        packet.address = InetAddress.getByName("255.255.255.255")
        try {
            socket.send(packet)
            debug("client send Broadcast ${buffer.size} bytes to ${packet.address}: ${HexString.valueOf(buffer)}")
        } catch (e: IOException) {
            e.printStackTrace()
        } finally {
            socket.broadcast = false
        }
    }

    fun getBroadcastAddress(): InetAddress{
        if(isWifiApEnabled(KitUtil.getApp())) {
            return InetAddress.getByName("192.168.43.255");
        }
        val wifi = KitUtil.getApp().applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
        val dhcp = wifi.dhcpInfo ?: return InetAddress.getByName("255.255.255.255")
        val broadcast = dhcp.ipAddress and dhcp.netmask or dhcp.netmask.inv()
        val quads = ByteArray(4)
        for (k in 0 until 4){
            quads[k] = (broadcast shr k * 8 and 0xFF).toByte()
        }
        return InetAddress.getByAddress(quads)
    }

    private fun isWifiApEnabled(context: Context): Boolean {
        try {
            val manager = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
            val method: Method = manager.javaClass.getMethod("isWifiApEnabled")
            return method.invoke(manager) as Boolean
        } catch (e: NoSuchMethodException) {
            e.printStackTrace()
        } catch (e: IllegalAccessException) {
            e.printStackTrace()
        } catch (e: IllegalArgumentException) {
            e.printStackTrace()
        } catch (e: InvocationTargetException) {
            e.printStackTrace()
        }
        return false
    }

    class Option {
        var clientPort: Int = 6700

        var servicePort: Int = 7700
    }

    companion object {
        var DEBUG = true
    }


}